#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QDebug>
#include <QTime>
#include <QFile>
#include <QMap>

#include <math.h>

#include "Graph.h"
#include "Node.h"
#include "NormalNode.h"
#include "BernoulliNode.h"
#include "PoissonNode.h"
#include "GammaNode.h"
#include "BetaNode.h"
#include "CombinationNode.h"
#include "Distribution.h"
#include "ToThePiNode.h"

#include "GraphWidget/GraphWidget.h"

#include <gsl/gsl_statistics.h>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

}

MainWindow::~MainWindow()
{
    delete ui;
}

Graph* MainWindow::createRealFaculty(QVector<double>& dataList)
{
    Graph* graph = new Graph();

    Node* priorMean = new Node(5);
    Node* priorStdDev = new Node(1.0/9.0);
    Node* meanNode = new NormalNode(priorMean, priorStdDev, Distribution::sharedInstance()->mean(dataList), false, "Mean");
    meanNode->setProposalStandardDeviation(0.2);
    graph->addNode(priorMean);
    graph->addNode(priorStdDev);
    graph->addNode(meanNode);

    Node* priorAlpha = new Node(11);
    Node* priorBeta = new Node(2.5);
    Node* varianceNode = new GammaNode(priorAlpha, priorBeta, true, Distribution::sharedInstance()->variance(dataList), false, "Variance");
    varianceNode->setProposalStandardDeviation(0.15);
    graph->addNode(priorAlpha);
    graph->addNode(priorBeta);
    graph->addNode(varianceNode);

    foreach (double data, dataList) {
        Node* normalNode = new NormalNode(meanNode, varianceNode, data, true);

        graph->addNode(normalNode);
    }

    return graph;
}

Graph* MainWindow::createPredictiveFacultyHyperMean(QVector<double>& dataList)
{
    Node* realData = new Node(0);
    realData->setSampleHistory(dataList);

    Graph* graph = new Graph();

    ///// LEARNING LAB
    Node* hyperMeanMean = new Node(5);
    Node* hyperMeanVariance = new Node(1);

    Node* priorMean = new NormalNode(hyperMeanMean, hyperMeanVariance, 5,false, "Prior Mean");

    Node* priorVariance = new Node(1.0/9.0);
    Node* meanNode = new NormalNode(priorMean, priorVariance, Distribution::sharedInstance()->mean(dataList), false, "Mean");
    meanNode->setProposalStandardDeviation(0.2);
    graph->addNode(priorMean);
    graph->addNode(priorVariance);
    graph->addNode(meanNode);

    Node* priorAlpha = new Node(11);
    Node* priorBeta = new Node(2.5);
    Node* varianceNode = new GammaNode(priorAlpha, priorBeta, true, Distribution::sharedInstance()->variance(dataList), false, "Variance");
    varianceNode->setProposalStandardDeviation(0.15);
    graph->addNode(priorAlpha);
    graph->addNode(priorBeta);
    graph->addNode(varianceNode);

    foreach (double data, dataList) {
        Node* normalNode = new NormalNode(meanNode, varianceNode, data, true);

        graph->addNode(normalNode);
    }

    return graph;
}

Graph* MainWindow::createPredictiveFaculty(QVector<double>& dataList)
{
    Node* realData = new Node(0);
    realData->setSampleHistory(dataList);

    Graph* graph = new Graph();

    ///// LEARNING LAB
    Node* hyperMeanMean = new Node(5);
    Node* hyperMeanVariance = new Node(1);


    Node* priorMean = new NormalNode(hyperMeanMean, hyperMeanVariance, 5,false, "Prior Mean");

    Node* hyperVarAlpha = new Node(10);
    Node* hyperVarBeta = new Node(1);

    Node* priorVariance = new GammaNode(hyperVarAlpha, hyperVarBeta, true, Distribution::sharedInstance()->variance(dataList), false, "Prior Variance");
    priorVariance->setProposalStandardDeviation(0.039);

    Node* meanNode = new NormalNode(priorMean, priorVariance, Distribution::sharedInstance()->mean(dataList), false, "Mean");
    meanNode->setProposalStandardDeviation(0.2);
    graph->addNode(priorMean);
    graph->addNode(priorVariance);
    graph->addNode(meanNode);


    Node* hyperAlphaMean = new Node(11);
    Node* hyperAlphaVar = new Node(0.00694);


    Node* priorAlpha = new NormalNode(hyperAlphaMean, hyperAlphaVar, 11, false, "Prior Alpha");

    Node* hyperBetaMean = new Node(2.5);
    Node* hyperBetaVar = new Node(0.00694);


    Node* priorBeta = new NormalNode(hyperBetaMean, hyperBetaVar, 2.5, false, "Prior Beta");
    Node* varianceNode = new GammaNode(priorAlpha, priorBeta, true, Distribution::sharedInstance()->variance(dataList), false, "Variance");
    varianceNode->setProposalStandardDeviation(0.15);
    graph->addNode(priorAlpha);
    graph->addNode(priorBeta);
    graph->addNode(varianceNode);

    foreach (double data, dataList) {
        Node* normalNode = new NormalNode(meanNode, varianceNode, data, true);

        graph->addNode(normalNode);
    }

    return graph;
}


void MainWindow::on_facultyButton_clicked()
{
    QString facultyData = QString("6.39,6.32,6.25,6.24,6.21,6.18,6.17,6.13,") +
            QString("6.00,6.00,5.97,5.82,5.81,5.71,5.55,5.50,5.39,") +
            QString("5.37,5.35,5.30,5.27,4.94,4.50");

    QVector<double> dataList;
    foreach (QString data, facultyData.split(",")) {
        dataList.append(data.toDouble());
    }

    Graph* predictiveHyperMean = this->createPredictiveFacultyHyperMean(dataList);
    Graph* predictive = this->createPredictiveFaculty(dataList);
    Graph* real = this->createRealFaculty(dataList);

    predictiveHyperMean->startGibbsMetropolis(ui->iterations->text().toInt(), 100);
    predictive->startGibbsMetropolis(ui->iterations->text().toInt(), 100);
    real->startGibbsMetropolis(ui->iterations->text().toInt(), 100);


    GraphWidget* gw1 = new GraphWidget();

    gw1->setTitle("Learning Lab");

    Node* priorMean = predictive->getNode("Prior Mean");
    Node* priorVariance = predictive->getNode("Prior Variance");
    Node* priorAlpha = predictive->getNode("Prior Alpha");
    Node* priorBeta = predictive->getNode("Prior Beta");
    Node* mean = real->getNode("Mean");
    Node* variance = real->getNode("Variance");
    Node* predMean = predictive->getNode("Mean");
    Node* predVar = predictive->getNode("Variance");
    Node* predAlpha = predictive->getNode("Prior Alpha");
    Node* predBeta = predictive->getNode("Prior Beta");


    gw1->graphHistogram(priorMean->histogramLine(), Qt::green, 1, "Prior Mean " + QString::number(priorMean->mean()));
    gw1->graphHistogram(priorVariance->histogramLine(), Qt::red, 1, "Prior Variance " + QString::number(priorVariance->mean()));
    gw1->graphHistogram(priorAlpha->histogramLine(), Qt::cyan, 1, "Prior Alpha " + QString::number(priorAlpha->mean()));
    gw1->graphHistogram(priorBeta->histogramLine(), Qt::magenta, 1, "Prior Beta " + QString::number(priorBeta->mean()));
    gw1->autoResizeWindowRect();
    gw1->show();

    GraphWidget* gw2 = new GraphWidget();
    gw2->graphHistogram(mean->histogramLine(), Qt::green, 1, "Old Mean " + QString::number(mean->mean()));
    gw2->graphHistogram(variance->histogramLine(), Qt::red, 1, "Old Variance " + QString::number(variance->mean()));
    gw2->setTitle("Without Hyper Parameters");
    gw2->autoResizeWindowRect();
    gw2->show();

    GraphWidget* gw3 = new GraphWidget();
    gw3->graphHistogram(predMean->histogramLine(), Qt::green, 1, "New Mean " + QString::number(predMean->mean()));
    gw3->graphHistogram(predVar->histogramLine(), Qt::red, 1, "New Variance " + QString::number(predVar->mean()));
    gw3->setTitle("With Hyper Parameters");
    gw3->autoResizeWindowRect();
    gw3->show();

    Node* predHyperMean = predictiveHyperMean->getNode("Mean");
    Node* predHyperMeanVar = predictiveHyperMean->getNode("Variance");

    GraphWidget* gw5 = new GraphWidget();
    gw5->graphHistogram(predHyperMean->histogramLine(), Qt::green, 1, "Mean with Hyper Param " + QString::number(predHyperMean->mean()));
    gw5->graphHistogram(predHyperMeanVar->histogramLine(), Qt::red, 1, "Fixed Variance " + QString::number(predHyperMeanVar->mean()));
    gw5->setTitle("With Hyper Mean and Fixed Variance");
    gw5->autoResizeWindowRect();
    gw5->show();


    /*Node* predHyperMean = predictiveHyperMean->getNoPOOPde("Mean");
    Node* predHyperVar  = predictiveHyperMean->getNode("Variance");

    GraphWidget* gw5 = new GraphWidget();
    gw5->graphHistogram(predHyperMean->histogramLine(), Qt::green, 1, "Mean with Hyper Param " + QString::number(predHyperMean->mean()));
    gw5->graphHistogram(predHyperVar->histogramLine(), Qt::red, 1, "Fixed Variance " + QString::number(predHyperVar->mean()));
    gw5->setTitle("With Hyper Mean and Fixed Variance");
    gw5->autoResizeWindowRect();
    gw5->show();*/

    delete predictive;
    delete real;

}

void MainWindow::on_golfersButton_clicked()
{
    QFile file(":/resources/golfers.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QMap<QString, Node*> golferMeans;
    QMap<QString, Node*> tournamentMeans;

    QMap<QString, QVector<double> > golferScores;
    QMap<QString, QVector<double> > tournamentScores;
    QMap<QString, double> golferAverages;
    QMap<QString, double> tournamentAverages;

    QStringList data;
    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        QString each(line);
        each = each.simplified();
        QStringList lineData = each.split(" ");

        QString name = lineData[0];
        QString score = lineData[1];
        QString tournament = lineData[2];

        if (golferScores.contains(name)) {
            golferScores[name].append(score.toDouble());
        } else {
            golferScores.insert(name, QVector<double>());
            golferScores[name].append(score.toDouble());
        }

        if (tournamentScores.contains(name)) {
            tournamentScores[tournament].append(score.toDouble());
        } else {
            tournamentScores.insert(tournament, QVector<double>());
            tournamentScores[tournament].append(score.toDouble());
        }

        golferMeans.insert(name, NULL);
        tournamentMeans.insert(tournament, NULL);
        data.append(each);
    }


    foreach (QString name, golferScores.keys()) {
        double average = 0.0;
        for (int i = 0; i < golferScores[name].count(); i++) {
            average += golferScores[name][i];
        }
        average /= golferScores[name].count();
        golferAverages.insert(name, average);
    }

    foreach (QString name, tournamentScores.keys()) {
        double average = 0.0;
        for (int i = 0; i < tournamentScores[name].count(); i++) {
            average += tournamentScores[name][i];
        }
        average /= tournamentScores[name].count();
        tournamentAverages.insert(name, average);
    }

    Graph* graph = new Graph();

    Node* priorTournamentMean = new Node(72);
    Node* priorTournamentVar = new Node(2.0);
    Node* tournamentMean = new NormalNode(priorTournamentMean, priorTournamentVar, 72.8);
    graph->addNode(priorTournamentMean);
    graph->addNode(priorTournamentVar);
    graph->addNode(tournamentMean);

    Node* priorTournamentAlpha = new Node(18);
    Node* priorTournamentBeta = new Node(1/0.015);
    Node* tournamentVariance = new GammaNode(priorTournamentAlpha, priorTournamentBeta, true, 3);
    graph->addNode(priorTournamentAlpha);
    graph->addNode(priorTournamentBeta);
    graph->addNode(tournamentVariance);

    foreach (QString key, tournamentMeans.keys()) {
        tournamentMeans.insert(key, new NormalNode(tournamentMean, tournamentVariance, 72.8));
    }

    graph->addNodes(tournamentMeans.values());

    Node* priorGolferAlpha = new Node(18);
    Node* priorGolferBeta = new Node(1/0.015);
    Node* golferVariance = new GammaNode(priorGolferAlpha, priorGolferBeta, true, 3.5);
    Node* priorGolferMean = new Node(0);
    graph->addNode(priorGolferMean);
    graph->addNode(priorGolferAlpha);
    graph->addNode(priorGolferBeta);
    graph->addNode(golferVariance);

    foreach (QString name, golferMeans.keys()) {
        golferMeans.insert(name, new NormalNode(priorGolferMean, golferVariance, 0));
    }

    graph->addNodes(golferMeans.values());

    Node* priorGTAlpha = new Node(83);
    Node* priorGTBeta = new Node(1/0.0014);

    Node* gtVariance = new GammaNode(priorGTAlpha, priorGTBeta, true, 3.1);
    graph->addNode(priorGTAlpha);
    graph->addNode(priorGTBeta);
    graph->addNode(gtVariance);

    foreach (QString line, data) {
        QStringList lineData = line.split(" ");

        QString name = lineData[0];
        QString score = lineData[1];
        QString tournament = lineData[2];

        Node* combo = new CombinationNode(tournamentMeans[tournament], golferMeans[name]);
        Node* node = new NormalNode(combo, gtVariance, score.toDouble(), true);
        tournamentMeans[tournament]->addChild(node);
        golferMeans[name]->addChild(node);
        graph->addNode(node);
    }

    graph->startGibbsMetropolis(ui->iterations->text().toInt(), 100);

    GraphWidget* graphWidget = new GraphWidget();
//    graphWidget->graphLine(GraphWidgetLine(golferMeans["VijaySingh"]->getSampleHistory(), 0, 1, Qt::red));
//    graphWidget->graphLine(GraphWidgetLine(golferMeans["TimTims"]->getSampleHistory(), 0, 1, Qt::blue));
//    graphWidget->setTitle("Vijay Singh Mean: " +
//                          QString::number(golferMeans["VijaySingh"]->mean()) +
//                          " Tim Tims Mean: " +
//                          QString::number(golferMeans["TimTims"]->mean()));
//    GraphWidgetLine gl(golferMeans["VijaySingh"]->histogramLine(), Qt::red);
//    gl.setAsHistogram(true);
//    graphWidget->graphLine(gl);
//    graphWidget->setTitle("Vijay Singh Mean: " +
//                          QString::number(golferMeans["VijaySingh"]->mean()));

    foreach (QString name, golferMeans.keys()) {
        qDebug()  << name << "," << golferMeans[name]->mean() << ", " << golferMeans[name]->minimum() << ", " << golferMeans[name]->maximum();
    }

    graphWidget->autoResizeWindowRect();
    graphWidget->show();

    delete graph;

}

void MainWindow::on_burglaryButton_clicked()
{
    Graph* graph = new Graph();

    BernoulliNode* burglary = new BernoulliNode(0.0);
    //burglary->addTableEntry("1", 0.001);
    burglary->addTableEntry("1", 0.2);

    BernoulliNode* earthquake = new BernoulliNode(0.0);
    //earthquake->addTableEntry("1", 0.002);
    earthquake->addTableEntry("1", 0.3);

    BernoulliNode* alarm = new BernoulliNode(burglary, 1);
    alarm->addParent(earthquake);
    alarm->addTableEntry("11", 0.95);
    alarm->addTableEntry("10", 0.94);
    alarm->addTableEntry("01", 0.29);
    //alarm->addTableEntry("00", 0.001);
    alarm->addTableEntry("00", 0.2);

    BernoulliNode* john = new BernoulliNode(alarm, 0);
    john->addTableEntry("1", 0.9);
    //john->addTableEntry("0", 0.05);
    john->addTableEntry("0", 0.2);

    BernoulliNode* mary = new BernoulliNode(alarm, 0);
    mary->addTableEntry("1", 0.7);
    //mary->addTableEntry("0", 0.01);
    mary->addTableEntry("0", 0.3);

    graph->addNode(burglary);
    graph->addNode(earthquake);
    graph->addNode(alarm);
    graph->addNode(john);
    graph->addNode(mary);

    graph->startGibbsMetropolis(ui->iterations->text().toInt(), 1);

    Graph* learn = new Graph();
    Node* alpha = new Node(1);
    Node* beta = new Node(1);
    Node* alpha2 = new Node(1);
    Node* beta2 = new Node(1);

    Node* gAlpha = new Node(20.5);
    Node* gBeta = new Node(.01);
    Node* burgBetaAlpha = new GammaNode(gAlpha, gBeta, false, 0.25);

    Node* burgBeta = new BetaNode(alpha, beta, 0.001);
    Node* earthBeta = new BetaNode(alpha2, beta2, 0.002);

    for (int i = 0; i < burglary->getSampleHistory().count(); i++) {
        BernoulliNode* burg = new BernoulliNode(burgBeta, burglary->getSampleHistory()[i], true);
        //burg->addTableEntry("1", 0.2);

        BernoulliNode* burg = new BernoulliNode(burgBeta, burglary->getSampleHistory()[i], true);
        //burg->addTableEntry("1", 0.2);

        BernoulliNode* earth = new BernoulliNode(earthquake->getSampleHistory()[i], true);
        //earth->addTableEntry("1", 0.3);

        int num = rand() % 2;
        BernoulliNode* al = new BernoulliNode(alarm->getSampleHistory()[i], true);
        al->addParent(burg);
        al->addParent(earth);
        al->addTableEntry("11", 0.95);
        al->addTableEntry("10", 0.94);
        al->addTableEntry("01", 0.29);
        al->addTableEntry("00", 0.2);

        num = rand() % 2;
        BernoulliNode* jo = new BernoulliNode(john->getSampleHistory()[i], true);
        jo->addTableEntry("1", 0.9);
        jo->addTableEntry("0", 0.05);

        //mary->getSampleHistory()[i]
        BernoulliNode* ma = new BernoulliNode(mary->getSampleHistory()[i], true);
        ma->addTableEntry("1", 0.7);
        ma->addTableEntry("0", 0.01);

        learn->addNode(burg);
        learn->addNode(burg);
        learn->addNode(earth);
        learn->addNode(al);
        learn->addNode(jo);
        learn->addNode(ma);
    }
    learn->addNode(burgBeta);
//    learn->addNode(burgBeta);
    learn->addNode(earthBeta);
    //learn->addNode(burgBetaAlpha);

    learn->startGibbsMetropolis(ui->iterations->text().toInt(), 1);


    Graph* q = new Graph();
    // LAST FREAKING PART
    BernoulliNode* bg = new BernoulliNode(burgBeta, 0.001);
    //bg->addTableEntry("1", 0.001);

    BernoulliNode* et = new BernoulliNode(0.0);
    et->addTableEntry("1", 0.002);

    BernoulliNode* am = new BernoulliNode(bg, 1);
    am->addParent(et);
    am->addTableEntry("11", 0.95);
    am->addTableEntry("10", 0.94);
    am->addTableEntry("01", 0.29);
    am->addTableEntry("00", 0.001);

    BernoulliNode* jn = new BernoulliNode(am, 0);
    jn->addTableEntry("1", 0.9);
    john->addTableEntry("0", 0.05);

    BernoulliNode* mr = new BernoulliNode(am, 1, true);
    mr->addTableEntry("1", 0.7);
    mr->addTableEntry("0", 0.01);

    q->addNode(bg);
    q->addNode(et);
    q->addNode(am);
    q->addNode(jn);
    q->addNode(mr);

    q->startGibbsMetropolis(ui->iterations->text().toInt(), 100);


    GraphWidget* gw = new GraphWidget();
    gw->setTitle("Mary Called, Burglary or Not");
    //gw->graphHistogram(burglary->histogramLine(), Qt::yellow, 1, "Burglary Hyper Hyper Alpha Mean" + QString::number(burgBetaAlpha->mean()));    
    gw->graphLine(burgBeta->histogramLine(), Qt::red, 1, "Burglary Mean " + QString::number(burgBeta->mean()));
   // gw->graphHistogram(earthBeta->histogramLine(), Qt::green, 1, "Earthquake Hyper Mean " + QString::number(earthBeta->mean()));
    gw->autoResizeWindowRect();
    gw->show();

    delete graph;
}

void MainWindow::on_wackyButton_clicked()
{
    Graph* graph = new Graph();
    Node* aMean = new Node(20);
    Node* aVar = new Node(1);
    Node* A = new NormalNode(aMean, aVar, 20);

    Node* eAlpha = new Node(1);
    Node* eBeta = new Node(1);
    Node* E = new BetaNode(eAlpha, eBeta, 1);

    Node* aToThePi = new ToThePiNode(A);
    Node* bBeta = new Node(7.0);
    Node* B = new GammaNode(aToThePi, bBeta, false, 1746);
    A->addChild(B);

    Node* D = new BetaNode(A, E);

    Node* C = new BernoulliNode(D);

    Node* F = new PoissonNode(D, 1);

    Node* G = new NormalNode(E, F);

    graph->addNode(aMean);
    graph->addNode(aVar);
    graph->addNode(A);
    graph->addNode(eAlpha);
    graph->addNode(eBeta);
    graph->addNode(E);
    graph->addNode(bBeta);
    graph->addNode(aToThePi);
    graph->addNode(B);
    graph->addNode(D);
    graph->addNode(C);
    graph->addNode(B);
    graph->addNode(F);
    graph->addNode(G);

    graph->startGibbsMetropolis(ui->iterations->text().toInt(), 1000);


    GraphWidget* graphWidget = new GraphWidget();
    graphWidget->setXIntervalCount(4);
    //graphWidget->graphLine(GraphWidgetLine(A->getSampleHistory(), 1000, 1, Qt::red));
    //graphWidget->graphLine(GraphWidgetLine(B->getSampleHistory(), 1000, 1, Qt::green));
    //graphWidget->graphLine(GraphWidgetLine(C->getSampleHistory(), 1000, 1, Qt::yellow));
//    GraphWidgetLine gl(F->histogramLine(), Qt::blue, 10.0f);
//    gl.setAsHistogram(true);
//    graphWidget->graphLine(gl);
    graphWidget->setTitle("F Mean: " + QString::number(F->mean()));

    graphWidget->autoResizeWindowRect();
    graphWidget->show();
    delete graph;
}

void MainWindow::on_myOwnButton_clicked()
{
    // Probability of getting a new tv

    Node* priceAlpha = new Node(2.5);
    Node* priceBeta = new Node(2);
    Node* price = new GammaNode(priceAlpha, priceBeta, false, 5);

    Node* expensesMean = new Node(12);
    Node* expensesVar = new Node(1);
    Node* expenses = new NormalNode(expensesMean, expensesVar, 10);

    Node* fdBeta = new Node(5);
    Node* familyDesire = new GammaNode(expenses, fdBeta, true, 9);

    Node* getTV = new BetaNode(price, familyDesire, .7);

    Graph* graph = new Graph();

    graph->addNode(priceAlpha);
    graph->addNode(priceBeta);
    graph->addNode(price);
    graph->addNode(expensesMean);
    graph->addNode(expensesVar);
    graph->addNode(expenses);
    graph->addNode(fdBeta);
    graph->addNode(familyDesire);
    graph->addNode(getTV);

    graph->startGibbsMetropolis(ui->iterations->text().toInt(), 1000);

    qDebug() << getTV->mean();

    GraphWidget* graphWidget = new GraphWidget();
    //graphWidget->graphLine(GraphWidgetLine(price->getSampleHistory(), 1000, 1, QColor(150, 0, 255)));

//    GraphWidgetLine gl(getTV->histogramLine(), Qt::blue);
//    gl.setAsHistogram(true);
//    graphWidget->graphLine(gl);
    graphWidget->setTitle("Get TV: " + QString::number(getTV->mean()));
    graphWidget->autoResizeWindowRect();
    graphWidget->show();

    delete graph;


}
